package com.gitee.wsl.func.filter.bloomfilter

import com.gitee.wsl.func.filter.bloomfilter.api.Hasher
import com.gitee.wsl.func.filter.bloomfilter.api.IndexExtractor
import com.gitee.wsl.func.filter.bloomfilter.bean.BitMaps.mod
import com.gitee.wsl.func.filter.bloomfilter.bean.Shape
import com.gitee.wsl.func.predicate.IntPredicate
import com.gitee.wsl.func.predicate.Predicate

/**
 * A Hasher that implements simple combinatorial hashing as described by
 * [Krisch and Mitzenmacher](https://www.eecs.harvard.edu/~michaelm/postscripts/tr-02-05.pdf).
 *
 *
 * To be used for testing only.
 */
class IncrementingHasher(
    /**
     * The initial hash value.
     */
    private val initial: Long,
    /**
     * The value to increment the hash value by.
     */
    private val increment: Long,
) : Hasher {
    public override fun indices(shape: Shape): IndexExtractor {

        return object : IndexExtractor {
            override fun asIndexArray(): IntArray {
                val result = IntArray(shape.numberOfHashFunctions)
                val idx = IntArray(1)

                // This method needs to return duplicate indices
                processIndices(Predicate { i: Int? ->
                    result[idx[0]++] = i!!
                    true
                })
                return result
            }

            public override fun processIndices(consumer: IntPredicate): Boolean {

                val bits: Int = shape.numberOfBits

                // Essentially this is computing a wrapped modulus from a start point and an
                // increment. So actually you only need two modulus operations before the loop.
                // This avoids any modulus operation inside the while loop. It uses a long index
                // to avoid overflow.
                var index = mod(initial, bits).toLong()
                val inc = mod(increment, bits)

                for (functionalCount in 0..<shape.numberOfHashFunctions) {
                    if (!consumer.test(index.toInt())) {
                        return false
                    }
                    index += inc.toLong()
                    index = if (index >= bits) index - bits else index
                }
                return true
            }
        }
    }
}
